﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MathLab.Messaging;
using System.Text.RegularExpressions;
using MathLab.Parsing;
using MathLab.Utils;
using MathLab.Exceptions;
using System.IO;

namespace MathLab.Interpretation
{
    /// <summary>
    /// Class that interprets commands and then processes them.
    /// </summary>
    class Interpreter : DataHolder
    {
        public Interpreter() : base()
        {
            
        }

        public void process(List<string> commands)
        {
            Console.WriteLine(Literals.COMMAND_SEPARATOR);

            foreach (string command in commands)
            {
                Console.WriteLine(Literals.ENDLN);

                if (isExit(command))
                {
                    Environment.Exit(0);
                }
                else if (isHelp(command))
                {
                    Console.WriteLine(Messages.HELP_MESSAGE());
                }
                else if (isReadFromFile(command))
                {
                    ReadFromFileParser parser = new ReadFromFileParser();
                    parser.parse(command);

                    ArrayCreateParser arrayCreateParser = new ArrayCreateParser();

                    try
                    {
                        using (StreamReader sr = File.OpenText(parser.fileName))
                        {
                            string s = "";
                            while ((s = sr.ReadLine()) != null)
                            {
                                arrayCreateParser.parse(s);

                                addArray(new Matrix(arrayCreateParser.arrayName, arrayCreateParser.array));
                                Console.WriteLine(Messages.ARRAY_ADDED(arrayCreateParser.arrayName));
                            }
                        }
                    }
                    catch (ArrayNotFoundException e)
                    {
                        Console.WriteLine(e.message);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
                else if (isWriteToFile(command))
                {
                    WriteToFileParser parser = new WriteToFileParser();
                    parser.parse(command);

                    try
                    {
                        using (StreamWriter sw = File.CreateText(parser.fileName))
                        {
                            foreach (string array in parser.arrays)
                            {
                                sw.WriteLine(StringUtils.arrayToFileString(getArray(array)));
                            }
                        }
                    }
                    catch (ArrayNotFoundException e)
                    {
                        Console.WriteLine(e.message);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }

                    Console.WriteLine(Messages.FILE_ADDED(parser.fileName));
                }
                else if (isArrayView(command))
                {
                    try
                    {
                        Console.WriteLine(command + Literals.EQUALS + Literals.ENDLN + getArrayAsString(command));
                    }
                    catch (ArrayNotFoundException e)
                    {
                        Console.WriteLine(e.message);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
                else if (isArrayCreate(command))
                {
                    ArrayCreateParser parser = new ArrayCreateParser();
                    parser.parse(command);

                    try
                    {
                        addArray(new Matrix(parser.arrayName, parser.array));
                        Console.WriteLine(Messages.ARRAY_ADDED(parser.arrayName));
                    }
                    catch (ArrayNotFoundException e)
                    {
                        Console.WriteLine(e.message);
                    }
                    /*catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }*/
                }
                else if (isArrayAddAndSave(command))
                {
                    ArrayOperationParser parser = new ArrayOperationParser();
                    parser.parse(command);

                    try
                    {
                        Matrix resultArray = getArray(parser.firstArrayName) + getArray(parser.secondArrayName);
                        resultArray.name = parser.resultArrayName;

                        addArray(resultArray);
                        Console.WriteLine(Messages.ARRAY_ADDED(parser.resultArrayName));
                    }
                    catch (ArrayNotFoundException e)
                    {
                        Console.WriteLine(e.message);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
                else if (isArrayAddAndDisplay(command))
                {
                    ArrayOperationParser parser = new ArrayOperationParser();
                    parser.parse(command);

                    try
                    {
                        Console.WriteLine(command + " " + Literals.EQUALS + Literals.ENDLN + getArrayAsString(getArray(parser.firstArrayName) + getArray(parser.secondArrayName)));
                    }
                    catch (ArrayNotFoundException e)
                    {
                        Console.WriteLine(e.message);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
                else if (isArrayScalarMultiplyAndSaveArrayFirst(command) || isArrayScalarMultiplyAndSaveDigitFirst(command))
                {
                    ArrayScalarMultiplyParser parser = new ArrayScalarMultiplyParser();
                    parser.parse(command);

                    try
                    {
                        Matrix resultArray = getArray(parser.firstArrayName) * parser.multiplier;
                        resultArray.name = parser.resultArrayName;

                        addArray(resultArray);
                        Console.WriteLine(Messages.ARRAY_ADDED(parser.resultArrayName));
                    }
                    catch (ArrayNotFoundException e)
                    {
                        Console.WriteLine(e.message);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
                else if (isArrayScalarMultiplyAndDisplayArrayFirst(command) || isArrayScalarMultiplyAndDisplayDigitFirst(command))
                {

                    ArrayScalarMultiplyParser parser = new ArrayScalarMultiplyParser();
                    parser.parse(command);

                    try
                    {
                        Console.WriteLine(command + " " + Literals.EQUALS + Literals.ENDLN + getArrayAsString(getArray(parser.firstArrayName) * parser.multiplier));
                    }
                    catch (ArrayNotFoundException e)
                    {
                        Console.WriteLine(e.message);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
                else if (isArrayMultiplyAndSave(command))
                {
                    ArrayOperationParser parser = new ArrayOperationParser();
                    parser.parse(command);

                    try
                    {
                        Matrix resultArray = getArray(parser.firstArrayName) * getArray(parser.secondArrayName);
                        resultArray.name = parser.resultArrayName;

                        addArray(resultArray);
                        Console.WriteLine(Messages.ARRAY_ADDED(parser.resultArrayName));
                    }
                    catch (ArrayNotFoundException e)
                    {
                        Console.WriteLine(e.message);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
                else if (isArrayMultiplyAndDisplay(command))
                {
                    ArrayOperationParser parser = new ArrayOperationParser();
                    parser.parse(command);

                    try
                    {
                        Console.WriteLine(command + " " + Literals.EQUALS + Literals.ENDLN + getArrayAsString(getArray(parser.firstArrayName) * getArray(parser.secondArrayName)));
                    }
                    catch (ArrayNotFoundException e)
                    {
                        Console.WriteLine(e.message);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
                else if (isArrayInverseAndSave(command))
                {
                    ArraySpecificCommandParser parser = new ArraySpecificCommandParser();
                    parser.parse(command);

                    try
                    {
                        Matrix resultArray = new Matrix(parser.resultArrayName, MatrixInverse.InverseMatrix((getArray(parser.firstArrayName).matrix)));

                        addArray(resultArray);
                        Console.WriteLine(Messages.ARRAY_ADDED(parser.resultArrayName));
                    }
                    catch (ArrayNotFoundException e)
                    {
                        Console.WriteLine(e.message);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
                else if (isArrayInverseAndDisplay(command))
                {
                    ArraySpecificCommandParser parser = new ArraySpecificCommandParser();
                    parser.parse(command);

                    try
                    {
                        Console.WriteLine(command + " " + Literals.EQUALS + Literals.ENDLN + getArrayAsString(MatrixInverse.InverseMatrix((getArray(parser.firstArrayName).matrix))));
                    }
                    catch (ArrayNotFoundException e)
                    {
                        Console.WriteLine(e.message);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
                else if (isArrayTransposeAndSave(command))
                {
                    ArraySpecificCommandParser parser = new ArraySpecificCommandParser();
                    parser.parse(command);

                    try
                    {
                        Matrix resultArray = new Matrix(parser.resultArrayName, MatrixTransposition.Transpose((getArray(parser.firstArrayName).matrix)));

                        addArray(resultArray);
                        Console.WriteLine(Messages.ARRAY_ADDED(parser.resultArrayName));
                    }
                    catch (ArrayNotFoundException e)
                    {
                        Console.WriteLine(e.message);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
                else if (isArrayTransposeAndDisplay(command))
                {
                    ArraySpecificCommandParser parser = new ArraySpecificCommandParser();
                    parser.parse(command);

                    try
                    {
                        Console.WriteLine(command + " " + Literals.EQUALS + Literals.ENDLN + getArrayAsString(MatrixTransposition.Transpose((getArray(parser.firstArrayName).matrix))));
                    }
                    catch (ArrayNotFoundException e)
                    {
                        Console.WriteLine(e.message);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
                else if (isVectorMultiplyAndDisplay(command))
                {
                    ArrayOperationParser parser = new ArrayOperationParser();
                    parser.parse(command);

                    try
                    {
                        Console.WriteLine(command + " " + Literals.EQUALS + Literals.ENDLN + getArrayAsString(getArray(parser.firstArrayName) | getArray(parser.secondArrayName)));
                    }
                    catch (ArrayNotFoundException e)
                    {
                        Console.WriteLine(e.message);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
                else if (isVectorMultiplyAndSave(command))
                {
                    ArrayOperationParser parser = new ArrayOperationParser();
                    parser.parse(command);

                    try
                    {
                        Matrix resultArray = getArray(parser.firstArrayName) | getArray(parser.secondArrayName);
                        resultArray.name = parser.resultArrayName;

                        addArray(resultArray);
                        Console.WriteLine(Messages.ARRAY_ADDED(parser.resultArrayName));
                    }
                    catch (ArrayNotFoundException e)
                    {
                        Console.WriteLine(e.message);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
                else
                {
                    if (!"".Equals(command))
                    {
                        Console.WriteLine(Messages.SYNTAX_ERROR);
                    }
                }
                Console.WriteLine(Literals.COMMAND_SEPARATOR);
            }
        }

        private bool isArrayCreate(string command)
        {
            return Commands.ARRAY_CREATE.Match(command).Success;
        }

        private bool isArrayView(string command)
        {
            return Commands.ARRAY_VIEW.Match(command).Success;
        }

        private bool isExit(string command)
        {
            return Commands.EXIT.Equals(command);
        }

        private bool isReadFromFile(string command)
        {
            return Commands.READ_FROM_FILE.Match(command).Success;
        }

        private bool isWriteToFile(string command)
        {
            return Commands.WRITE_TO_FILE.Match(command).Success;
        }

        private bool isArrayAddAndSave(string command)
        {
            return Commands.ARRAY_ADD_AND_SAVE.Match(command).Success;
        }

        private bool isArrayAddAndDisplay(string command)
        {
            return Commands.ARRAY_ADD_AND_DISPLAY.Match(command).Success;
        }

        private bool isArrayMultiplyAndSave(string command)
        {
            return Commands.ARRAY_MULTIPLY_AND_SAVE.Match(command).Success;
        }

        private bool isArrayMultiplyAndDisplay(string command)
        {
            return Commands.ARRAY_MULTIPLY_AND_DISPLAY.Match(command).Success;
        }

        private bool isArrayInverseAndSave(string command)
        {
            return Commands.ARRAY_INVERSE_AND_SAVE.Match(command).Success;
        }

        private bool isArrayInverseAndDisplay(string command)
        {
            return Commands.ARRAY_INVERSE_AND_DISPLAY.Match(command).Success;
        }

        private bool isArrayTransposeAndSave(string command)
        {
            return Commands.ARRAY_TRANSPOSE_AND_SAVE.Match(command).Success;
        }

        private bool isArrayTransposeAndDisplay(string command)
        {
            return Commands.ARRAY_TRANSPOSE_AND_DISPLAY.Match(command).Success;
        }

        private bool isArrayScalarMultiplyAndSaveArrayFirst(string command)
        {
            return Commands.ARRAY_SCALAR_MULTIPLY_AND_SAVE_ARRAY_FIRST.Match(command).Success;
        }

        private bool isArrayScalarMultiplyAndDisplayArrayFirst(string command)
        {
            return Commands.ARRAY_SCALAR_MULTIPLY_AND_DISPLAY_ARRAY_FIRST.Match(command).Success;
        }

        private bool isArrayScalarMultiplyAndSaveDigitFirst(string command)
        {
            return Commands.ARRAY_SCALAR_MULTIPLY_AND_SAVE_DIGIT_FIRST.Match(command).Success;
        }

        private bool isArrayScalarMultiplyAndDisplayDigitFirst(string command)
        {
            return Commands.ARRAY_SCALAR_MULTIPLY_AND_DISPLAY_DIGIT_FIRST.Match(command).Success;
        }

        private bool isHelp(string command)
        {
            return Commands.HELP.Equals(command);
        }

        private bool isVectorMultiplyAndSave(string command)
        {
            return Commands.VECTOR_SCALAR_MULTIPLY_AND_SAVE.Match(command).Success;
        }

        private bool isVectorMultiplyAndDisplay(string command)
        {
            return Commands.VECTOR_SCALAR_MULTIPLY_AND_DISPLAY.Match(command).Success;
        }

    }
}
